package compiler.ast;

import java.io.PrintStream;
import java.util.ArrayList;

import compiler.symboltable.SymbolTable;
import compiler.tac.BasicBlock;
import compiler.tac.EntryBlock;
import compiler.tac.ExitBlock;
import compiler.tac.ThreeAddressCode;

public class Module {
	Identifier name;
	SymbolTable vars;
	ArrayList<FuncDeclaration> funcs;
	ArrayList<Statement> body;
	
	public Module(Identifier name, SymbolTable vars, ArrayList<FuncDeclaration> funcs, ArrayList<Statement> body) {
		this.name = name;
		this.vars = vars;
		this.funcs = funcs;
		this.body = body;
	}
	public Module(Identifier name)
	{
		this.name = name;	
	}
	
	public void printDotFormat(PrintStream out, String node) {
		out.println(node+" [label=\"{module|"+name.name()+"}\"];");
		
		int order = 0;
		for (FuncDeclaration func : funcs) {
			out.println(node+"->"+node+order+";");
			func.printDotFormat(out, node+order);
			order++;
		}
		
		String bodyNode = node+order;
		out.println(node+"->"+bodyNode+";");
		out.println(bodyNode+" [label=\"body\"];");
		
		order = 0;
		for (Statement stmt : body) {
			out.println(bodyNode+"->"+bodyNode+order+";");
			stmt.printDotFormat(out, bodyNode+order);
			order++;
		}
	}
	
	public ArrayList<FuncDeclaration> getFuncs() {
		return funcs;
	}
	public void setFuncs(ArrayList<FuncDeclaration> f) {
		funcs = f;
	}
	
	public ArrayList<Statement> getBody()
	{
		return body;
	}
	public void setBody(ArrayList<Statement> b)
	{
		body = b;
	}
	public void setVars(SymbolTable t)
	{
		vars = t;
	}
	public SymbolTable getVars()
	{
		return vars;
	}
	
	public ThreeAddressCode toThreeAddressCode() {
		ThreeAddressCode ret = new ThreeAddressCode();
		ret.setST(this.vars);
		// functions
		if(funcs != null)
			for(int i = 0; i < funcs.size(); i ++)
			{
				funcs.get(i).toThreeAddressCode(ret);
			}
		// module body
		EntryBlock entryb = new EntryBlock(name.name());
		ret.setEntry(entryb);
		
		ExitBlock exitb = new ExitBlock();
		ret.setExit(exitb);
		
		// TODO: Do we need to add an edge from entry block to exit block?
		
		BasicBlock basicb = new BasicBlock();
		BasicBlock.add(entryb, basicb);
		ret.addBlock(basicb);
		
		for (Statement stmt : body) {
			basicb = stmt.toThreeAddressCode(ret, basicb, exitb);
		}

		
		basicb.addEndInstruction();
		
		return ret;
	}
}
